<template>
  <a-modal
    v-model:visible="visible"
    :title="title"
    class="custom_dialog">
    <a-form
      ref="refForm"
      :label-align="labelAlign"
      :label-col="labelCol"
      :wrapper-col="wrapperCol"
      :model="formConfig.state"
      :rules="rules">
      <template
        v-for="(item, index) in formConfig.formItems.filter((item)=> !item.ignore)"
        :key="index">
        <template v-if="item.custom">
          <a-form-item
            :label="item.label"
            :name="item.key">
            <slot
              :name="item.key"
              :state="formConfig.state"/>
          </a-form-item>
        </template>
        <template v-else>
          <a-form-item
            :label="item.label"
            :name="item.key">
            <component
              :is="item.type === 'select'?'ASelect':'AInput'"
              v-model:value="formConfig.state[item.key]"
              :options="item.options"
              autocomplete="off"
              :disabled="item.disabled"
              :placeholder="item.placeholder"/>
          </a-form-item>
        </template>
      </template>
    </a-form>
    <template #footer>
      <a-button
        key="back"
        @click="cancel">取消</a-button>
      <a-button
        key="submit"
        type="primary"
        :disabled="confirmDisabled"
        @click="confirm">确定</a-button>
    </template>
  </a-modal>

</template>
<script setup>
import { ref, reactive, defineProps, watch, defineExpose, defineEmits, computed } from 'vue'
import { message } from 'ant-design-vue'
/** 对外暴露的属性 */
const props = defineProps({
  // 标题
  title: {
    type: String,
    default: ''
  },
  /**
   * form配置 数组元素结构 { key, value, label, type, custom, options, ignore }
   * key: 字段名称
   * value: 默认值
   * label: 标签
   * type: 组件类型 默认是input，只支持 input 和 select，当custom为true时 该属性会被忽略
   * custom: 是否自定义, 值为true, html如下就能实现自定义
   *         <BaseFormDialog><template v-slot:{key}={state}>{自定义内容}</template></BaseFormDialog>
   * options: type为select时有效， select的options
   * ignore: 是否忽略本字段  true时不会在form表单中渲染内容
   */
  form: {
    type: Array,
    default: undefined
  },
  // 表达校验规则
  rules: {
    type: Object,
    default: () => ({})
  },
  // 标签对齐位置
  labelAlign: {
    type: String,
    default: 'right'
  },
  //  标签列样式
  labelCol: {
    type: Object,
    default: () => ({ style: { width: '110px' } })
  },
  // 内容列样式
  wrapperCol: {
    type: Object,
    default: () => ({ style: { marginLeft: '42px' }, span: 16 })
  },
  // 参数过滤 confirmRequestConfig传值时 请求发送前对参数进行过滤，confirmRequestConfig为undefined时 无效
  paramsFilter: {
    type: Function,
    default: undefined
  }
})
// 表单配置
const formConfig = reactive({
  state: {}, // form model
  formItems: [] // 用于渲染form item
})
const visible = ref(false) // dialog的显隐状态
const refForm = ref(null) // form表单组件实例

const emit = defineEmits(['confirm']) // dialog 确定按钮事件 点击确定按钮 表单校验成功后触发

const confirmDisabled = computed(() => {
  const { state } = formConfig
  const keys = Object.keys(state)
  let result = false
  keys.forEach((key) => {
    let value = state[key]
    if (value === undefined || (typeof value === 'string' && value.trim() === '')) result = true
  })
  return result
})

// 组件form属性监听事件  根据外部传入的form数组 创建表单配置
watch(() => props.form, (form) => {
  if (form) {
    let formState = {}
    let formItems = []
    form.forEach(({ key, value, label, type, custom, options, ignore, placeholder, disabled }) => {
      formState[key] = value
      formItems.push({
        key, label, type, custom, options, ignore, placeholder: placeholder || ((type === 'select' ? '请选择' : '请输入') + label), disabled
      })
    })
    formConfig.state = formState
    formConfig.formItems = formItems
  }
}, { immediate: true, deep: true })
/** 退出model时 */
const cancel = () => {
  refForm.value.$el.parentElement.scrollTop = 0
  visible.value = false
}
/**
 * dialog 确定按钮事件
 */
const confirm = () => {
  refForm.value.validate().then(() => {
    let { request, success, error } = confirmConfig
    if (request) {
      let filter = props.paramsFilter
      let formState = JSON.parse(JSON.stringify(formConfig.state))
      let params = filter ? filter(formState) : formState
      request(params).then(({ data, resCode, resMsg }) => {
        if (resCode === '00000') {
          message.success(resMsg)
          visible.value = false
          refForm.value.$el.parentElement.scrollTop = 0
          if (success) success({ data, resCode, resMsg })
        } else if (error) error(data, resCode, resMsg)
      })
    } else {
      emit('confirm', formConfig.state)
      visible.value = false
      refForm.value.$el.parentElement.scrollTop = 0
    }
  }).catch((err) => {
    console.error(err)
  })
}
/**
 * 显示dialog
 */
const show = (reset, formData) => {
  visible.value = true
  if (reset) resetForm()
  if (formData) setFormData(formData)
}
/**
 * 隐藏dialog
 */
const hide = () => {
  visible.value = false
}
/**
 * 重置form表单model和校验状态
 */
const resetForm = () => {
  if (refForm.value)refForm.value.resetFields()
}
/**
 * 获取form model
 */
const getFormData = () => formConfig.state
/**
 * 设置表单数据
 */
const setFormData = (data) => {
  resetForm()
  const keys = Object.keys(data)
  keys.forEach((key) => {
    formConfig.state[key] = data[key]
  })
}
let confirmConfig = {}
/**
 * @param {Function} request 确定按钮请求
 * @param { Function } successCallback 请求成功回调
 * @param { Function } errorCallback 请求失败回调
 */
const setRequest = (request, successCallback, errorCallback) => {
  if (request instanceof Function) {
    confirmConfig.request = request
    confirmConfig.success = successCallback
    confirmConfig.error = errorCallback
  } else {
    console.error('argument is a Function witch return a request Promise')
  }
}
/** 更新form item 配置 */
const updateFormItem = (prop, valueObj) => {
  const formItem = formConfig.formItems.filter((item) => item.key === prop)[0]
  if (formItem) {
    Object.assign(formItem, valueObj)
  }
}
/**
 * 对外暴露接口
 */
defineExpose({
  show,
  hide,
  getFormData,
  setFormData,
  setRequest,
  updateFormItem
})
</script>
